﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;

using Oracle.ManagedDataAccess.Client;
using System.Data.Common;

namespace Dxr.Data.Oracle
{
    public class OracleDataAccess : ObjectAccess
    {
        #region 字段
        private Dictionary<string, OracleDbType> _dbTypes;
        #endregion

        #region 构造函数
        public OracleDataAccess()
            : base(OracleClientFactory.Instance)
        {
        }
        #endregion

        #region 属性
        private Dictionary<string, OracleDbType> DbTypes
        {
            get
            {
                if (_dbTypes == null)
                    _dbTypes = Enum.GetValues(typeof(OracleDbType)).Cast<OracleDbType>().ToDictionary(p => p.ToString().ToUpper(), p => p);
                return _dbTypes;
            }
        }
        #endregion

        #region 重写方法
        protected override string CreateSelectSql(CreateSelectSqlParameter parameter)
        {
            var sql = string.Format("SELECT {0} FROM {1}", string.Join(",", parameter.Columns.Select(p => p.ToSelectColumn())), parameter.Info.GetTableName());
            if (!string.IsNullOrEmpty(parameter.Join))
                sql += " " + parameter.Join;
            if (!string.IsNullOrEmpty(parameter.Where))
                sql += " " + parameter.Where;

            HandleSql(ref sql, parameter);

            if (parameter.Paging != null && parameter.Paging.PageSize > 0)
            {
                if (parameter.Paging.PageIndex < 1)
                    parameter.Paging.PageIndex = 1;

                sql = string.Format("select * from (select A.* , ROWNUM RN from ({0}) A where ROWNUM <= {1}) where RN > {2}", sql, parameter.Paging.PageSize * parameter.Paging.PageIndex, parameter.Paging.PageSize * (parameter.Paging.PageIndex - 1));
            }

            return sql;
        }

        protected override string CreateSelectSql(CreateGroupSelectSqlParameter parameter)
        {
            return CreateSelectSql((CreateSelectSqlParameter)parameter);
        }

        private void HandleSql(ref string sql, CreateSelectSqlParameter parameter)
        {
            var gParameter = parameter as CreateGroupSelectSqlParameter;

            if (gParameter == null)
            {
                if (!string.IsNullOrEmpty(parameter.Orderby))
                    sql += " " + parameter.Orderby;
            }
            else
            {
                if (!string.IsNullOrEmpty(gParameter.Group))
                    sql += " " + gParameter.Group;

                if (!string.IsNullOrEmpty(gParameter.Having))
                    sql += " " + gParameter.Having;

                if (!string.IsNullOrEmpty(gParameter.Orderby))
                    sql += " " + gParameter.Orderby;

                string newColumns = string.Empty;
                if (gParameter.GroupedSelectColumns != null && gParameter.GroupedSelectColumns.Count > 0)
                {
                    newColumns = string.Join(",", gParameter.GroupedSelectColumns.Select(p =>
                    {
                        if (p.ClassInfo == parameter.Info)
                            return p.ToSelectColumn(gParameter.NewTableNameEx, p.ToColumn());
                        else
                            return p.ToSelectColumn();
                    }));
                    sql = string.Format("select {0} from ({1}) {2}", newColumns, sql, gParameter.NewTableNameEx);
                    if (!string.IsNullOrEmpty(gParameter.GroupedJoin))
                        sql += " " + gParameter.GroupedJoin;
                }
            }
        }

        public override DbParameter CreateParameter(int index, object value, string dbType = null, string name = null, bool isOutPut = false, bool isInOutPut = false, int? size = null, bool isProcedure = false)
        {
            var paramer = new OracleParameter();

            paramer.ParameterName = string.IsNullOrEmpty(name) ? ("p" + index) : name;
            if (!isProcedure)
                paramer.ParameterName = ":" + paramer.ParameterName;

            if (value is byte[])
            {
                paramer.Value = value;
            }
            else
                paramer.Value = SqlExecuter.ConvertValue(value);

            if (!string.IsNullOrEmpty(dbType))
            {
                if (DbTypes.ContainsKey(dbType.ToUpper()))
                    paramer.OracleDbType = DbTypes[dbType.ToUpper()];
            }

            if (size.HasValue)
                paramer.Size = size.Value;
            if (isOutPut)
                paramer.Direction = ParameterDirection.Output;
            else if (isInOutPut)
                paramer.Direction = ParameterDirection.InputOutput;

            return paramer;
        }
        #endregion

        protected override Dictionary<string, ColumnInfo> CreateColumnInfo(IEnumerable<string> columns, ClassInfo root)
        {
            return Oracle.OracleColumnInfo.Create(columns, root);
        }

        protected override ColumnInfo CreateColumnInfo(string original)
        {
            return new Oracle.OracleColumnInfo(original);
        }

        protected override ClassInfo CreateClassInfo(string @as, Mapping.ClassNode classNode)
        {
            return new Oracle.OracleClassInfo(@as, classNode);
        }

        protected override string GetProcedureName(Mapping.ProcedureNode procedureNode)
        {
            return procedureNode.GetProcedureName(false);
        }
    }
}
